
#pragma once
#ifndef _BOARD_H
#define _BOARD_H

#include <ostream>

#include <boost/serialization/is_bitwise_serializable.hpp>
#include <boost/serialization/access.hpp>
#include <boost/mpi.hpp>

namespace utils
{


	//////////////////////////////////////////////////////////////////////////
	//
	class Board
	{

		friend class boost::serialization::access;

		//Pocet bunek v poli, pro 2 je max. velikost Boardu 11x11, pro 4 16x16.
		static const unsigned STATIC_SIZE = 4;

		//Typ polozek v poli, drzicich bity.
		typedef unsigned long long backing_t;

		//Tato konstanta se rovna logaritmu poctu bitu v backing typu (64),
		//coz je zaroven velikost posuvu vpravo, ktery odpovida deleni poctem bitu.
		static const backing_t BITSHIFT=6;

		//Maska s poctem jednicek zprava, odpovidajici BITSHIFT.
		//Operaci AND s touto konstantou odpovida operaci modulo velikost backing typu (64).
		static const backing_t MODULO_MASK=0x3FULL;

		//Pole obsahujici jednotlive bity, pouzita staticka velikost,
		//za mensi univerzalnost ziskame rychlejsi serializaci a vytvoreni objektu.
		backing_t _backingArray[STATIC_SIZE];

		//Pocet policek (bitu).
		unsigned _size;

		//////////////////////////////////////////////////////////////////////////
		//Index bitu v bloku, jenz odpovida zadanemu globalnimu indexu bitu.
		inline unsigned bit_index(const unsigned i) const
		{
			//operace AND s cislem se 6 jednickami vpravo (0x3F) odpovida zbytky po deleni 64
			return i&MODULO_MASK;
		}

		//////////////////////////////////////////////////////////////////////////
		//Index bloku v backing array, odpovidajici zadanemu globalnimu indexu.
		inline unsigned block_index(const unsigned i) const
		{
			//posun o 6 doprava odpovida deleni 64, coz odpovida poctu bitu v unsigned long long
			return i >> BITSHIFT;
		}

		//////////////////////////////////////////////////////////////////////////
		//
		template<class Archive>
		inline void serialize(Archive& ar, const unsigned int version)
		{
			ar & _backingArray & _size;
		}

	public:
		//////////////////////////////////////////////////////////////////////////
		//Default ctor
		Board():_size(0)
		{}

		//////////////////////////////////////////////////////////////////////////
		//Ctor pro danou velikost.
		Board(unsigned s): _size(s)
		{
			if (s>sizeof(backing_t)*8*STATIC_SIZE)
			{
				throw "Pozadovana velikost je vetsi nez maximalni mozna.";
			}
			for(unsigned i = 0; i<STATIC_SIZE; ++i)
			{
				_backingArray[i] = 0ULL;
			}
		}

		//////////////////////////////////////////////////////////////////////////
		//Copy ctor.
		Board(const Board &that)
		{
			this->_size = that._size;
			for (unsigned i = 0; i<STATIC_SIZE; i++)
			{
				this->_backingArray[i] = that._backingArray[i];
			}
		}

		//////////////////////////////////////////////////////////////////////////
		//Operator prirazeni
		Board& operator= (const Board &b)
		{
			_size = b._size;
			for (unsigned i=0; i<STATIC_SIZE; ++i)
			{
				_backingArray[i] = b._backingArray[i];
			}
			return *this;
		}

		//////////////////////////////////////////////////////////////////////////
		//Vraci true, kdyz neni zadny bit nastaven na true.
		bool none() const {
			return !any();
		}

		//////////////////////////////////////////////////////////////////////////
		//Vraci true, kdyz je aspon 1 bit nekde nastaven na true.
		bool any() const
		{
			for (unsigned i = 0; i<STATIC_SIZE; ++i)
			{
				if (_backingArray[i])
				{
					return true;
				}
			}
			return false;
		}

		//////////////////////////////////////////////////////////////////////////
		//Hodnota bitu na i-te pozici
		bool at(const unsigned i) const
		{
			if (i>=_size)
			{
				throw "Index mimo rozsah";
			}
			return (_backingArray[block_index(i)] >> bit_index(i)) & 1L;
		}

		//////////////////////////////////////////////////////////////////////////
		//Operator[] je implementovan jen pro cteni, pro zapis pouzit metodu setAt.
		bool operator[] (const unsigned i) const
		{
			return at(i);
		}

		//////////////////////////////////////////////////////////////////////////
		//Nastavi bit na i-te pozici na hodnotu b.
		void setAt(const unsigned i, const bool b)
		{
			if (i>=_size)
			{
				throw "Index mimo rozsah";
			}

			if (b)
			{
				backing_t tmp = 1ULL << bit_index(i);
				_backingArray[block_index(i)] |= tmp;
			}
			else
			{
				backing_t tmp = ~(1ULL << bit_index(i));
				_backingArray[block_index(i)] &= tmp;
			}
		}

		//////////////////////////////////////////////////////////////////////////
		//Operator porovnani.
		bool operator== (const Board &b) const
		{
			if (_size!=b._size)
			{
				return false;
			}
			for (unsigned i = 0; i<STATIC_SIZE; ++i)
			{
				if (_backingArray[i] != b._backingArray[i])
				{
					return false;
				}
			}
			return true;
		}

		bool operator!= (const Board& that) const
		{
			return !(*this==that);
		}

		//////////////////////////////////////////////////////////////////////////
		//Pocet bitu v bitovem poli
		unsigned size() const
		{
			return _size;
		}

		//////////////////////////////////////////////////////////////////////////
		//Dtor.
		virtual ~Board()
		{
		}

		//////////////////////////////////////////////////////////////////////////
		//printing
		friend std::ostream& operator<< (std::ostream& os, const Board& b)
		{
			for (unsigned i = b._size; i>0; i--)
			{
				os << (b.at(i-1) ? '1' : '0');
			}
			return os;
		}
	};	//End of utils::Board


}	//End of namespace utils
//Makro, ktere prida trait, ze je trida serializovatelna po bitech,
//tedy neobsahuje pointry ani netreba zkoumat verzi a tak dal...
BOOST_IS_BITWISE_SERIALIZABLE(utils::Board);
namespace boost {
	namespace mpi {
		template <>
		struct is_mpi_datatype< utils::Board > : mpl::true_ {};
	}
}

#endif

